package com.awifi.dbus.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.awifi.dbus.dao.DevbusAccollectModelMapMapper;
import com.awifi.dbus.dao.DevbusAccollectOidMapper;
import com.awifi.dbus.model.*;
import com.awifi.dbus.util.http.HttpUtil;
import com.awifi.dbus.util.kafka.StringKafKaProducer;
import com.awifi.dbus.util.simplesnmp.SimpleSNMP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.VariableBinding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Vector;

/**
 * Created by yhy on 17-6-1.
 */
@Service
public class SNMPServiceImpl implements SNMPService {

    /**
     * 日志对象
     */
    private static Logger LOG = LoggerFactory.getLogger(SNMPServiceImpl.class);
    //获取 oid 信息
    @Autowired
    private DevbusAccollectOidMapper devbusAccollectOidMapper;
    //获取数据中心型号对应本地型号信息
    @Autowired
    private DevbusAccollectModelMapMapper devbusAccollectModelMapMapper;

    /**
     * pageSize
     */
    @Value("${pageSize}")
    private Integer pageSize;

    /**
     * 设备类型
     */
    @Value("${entityType}")
    private String entityType;

    /**
     * 权限中心给的第三方授权码
     */
    @Value("${auth.code}")
    private String oauthCode;

    /**
     * 获取token的url
     */
    @Value("${get.token.url}")
    private String getTokenUrl;


    /**
     * 获取token的url
     */
    @Value("${get.token.timeout}")
    private int getTokenUrlTimeout;


    /**
     * 获取设备及虚拟信息的url
     */
    @Value("${get.entity.info.url}")
    private String getEntityInfoUrl;

    /**
     * 获取设备及虚拟信息的url的超时时间
     */
    @Value("${get.entity.info.timeout}")
    private int getGetEntityInfoTimeout;

    /**
     * 是否开始严格模式
     */
    @Value("${snmp.strict}")
    private boolean strict;

    @Value("${kafka.brokerList}")
    private String brokerList;


    /**
     * 成功标识
     */
    private static final String SUCCESS = "success";

    @Override
    public void work() {
        //==========获取数据中心鉴权 token=============
        String token = getToken();

        //==========获取Ac 设备信息，并采集数据==================
        DevInfoParamsDTO devInfoParamsDTO = new DevInfoParamsDTO();
        devInfoParamsDTO.setEntityType(entityType);
        devInfoParamsDTO.setOutTypeId("AWIFI");
        devInfoParamsDTO.setPageSize(pageSize);

        int pageNum = 1;
        List<DevInfoDTO> entityInfoList;
        try {
            do {
                //-----------------分页获取ac设备信息----------------
                devInfoParamsDTO.setPageNum(pageNum);
                entityInfoList = getEntityInfoList(token, devInfoParamsDTO);
                if (null == entityInfoList || entityInfoList.isEmpty()) {
                    LOG.error("分页查询ac设备及虚拟信息失败,请求参数：token = {}, devInfoParamsDTO = {},返回结果：entityInfoList = {}",
                            token, JSON.toJSONString(devInfoParamsDTO), JSON.toJSONString(entityInfoList));
                    break;
                }
                //----------------采集设备信息-----------------------
                for (DevInfoDTO entityInfo : entityInfoList) {
                    if (null == entityInfo) {
                        continue;
                    }
                    JSONObject jsonObject;
                    try {
                        jsonObject = collectDevice(entityInfo, strict);
                    } catch (CannotGetJdbcConnectionException e) {
                        LOG.error("jdbc 获取链接失败重试");
                        jsonObject = collectDevice(entityInfo, strict);
                    }
                    //-------------发送到数据中心---------
                    if (null != jsonObject) {
//                        System.out.println(String.format("==========采集到设备%s的数据================", entityInfo.getIpAddr()));
//                        System.out.println(jsonObject.toJSONString());
                        StringKafKaProducer stringKafKaProducer =
                                new StringKafKaProducer(brokerList, StringKafKaProducer.REQUEST_ACK.NO_WAIT);
                        stringKafKaProducer.sendMessage("test-topic",jsonObject.toJSONString());
                    }
                }
                pageNum++;

            } while (pageSize == entityInfoList.size());
            //----------------
        } catch (Exception e) {
            LOG.error("uncath error:{}", e.getMessage());
        }

    }

//    /**
//     * 获取所有的ac设备信息
//     * @return ac设备信息
//     * @author sucb
//     * @date 2017年6月7日下午5:18:07
//     */
//    private List<DevInfoDTO> getDevInfoList() {
//        //获取token
//        String token = getToken();
//
//        DevInfoParamsDTO devInfoParamsDTO = new DevInfoParamsDTO();
//        devInfoParamsDTO.setEntityType(entityType);
//        devInfoParamsDTO.setPageSize(pageSize);
//
//        int pageNum = 1;
//        List<DevInfoDTO> devInfolist = new ArrayList<>();
//        List<DevInfoDTO> entityInfoList;
//        //分页获取ac设备信息
//        do {
//            devInfoParamsDTO.setPageNum(pageNum);
//            entityInfoList = getEntityInfoList(token, devInfoParamsDTO);
//            if(null == entityInfoList || entityInfoList.isEmpty()) {
//                LOG.error("分页查询ac设备及虚拟信息,请求参数：token = {}, devInfoParamsDTO = {},返回结果：entityInfoList = {}",
//                        token, JSON.toJSONString(devInfoParamsDTO), JSON.toJSONString(entityInfoList));
//                break;
//            }
//            for(DevInfoDTO entityInfo : entityInfoList){
//                if(null == entityInfo) {
//                    continue;
//                }
//                devInfolist.add(entityInfo);
//            }
//            pageNum++;
//        } while (pageSize == entityInfoList.size());
//
//        return devInfolist;
//    }

    /**
     * 分页查询ac设备及虚拟信息
     *
     * @param token            token
     * @param devInfoParamsDTO 分页查询的参数dto
     * @return 某一个的所有ac设备信息
     * @author sucb
     * @date 2017年6月7日下午5:17:55
     */
    private List<DevInfoDTO> getEntityInfoList(String token, DevInfoParamsDTO devInfoParamsDTO) {
        List<DevInfoDTO> entityInfoList;
        String res = HttpUtil.getEntityInfoList(getEntityInfoUrl,
                JSON.toJSONString(devInfoParamsDTO), token, getGetEntityInfoTimeout);
        LOG.info("分页查询ac设备及虚拟信息的返回结果,res = {}.", res);
        ResultDTO resDTO;
        try {
            resDTO = JSON.parseObject(res, ResultDTO.class);
        } catch (Exception e) {
            LOG.error("分页查询ac设备及虚拟信息结果转换异常,res = {}.", res);
            return null;
        }
        if (null == resDTO || !resDTO.isSuc()) {
            LOG.error("分页查询ac设备及虚拟信息结果不合法, res = {}", res);
            return null;
        }

        try {
            entityInfoList = JSON.parseArray(resDTO.getRs(), DevInfoDTO.class);
            return entityInfoList;
        } catch (Exception e) {
            LOG.error("分页查询ac设备及虚拟信息结果中rs字段转换异常,rs = {}.", resDTO.getRs());
            return null;
        }
    }

    /**
     * 获取token
     *
     * @return token
     * @author sucb
     * @date 2017年6月6日下午9:02:07
     */
    private String getToken() {
        String tokenRs = HttpUtil.getToken(getTokenUrl, oauthCode, getTokenUrlTimeout);
        LOG.info("权限中心获取token的返回结果,tokenRs = {}.", tokenRs);
        TokenRsDTO devInfoRsDTO = null;
        try {
            devInfoRsDTO = JSON.parseObject(tokenRs, TokenRsDTO.class);
        } catch (Exception e) {
            LOG.error("权限中心获取token的返回结果转换异常,tokenRs = {}.", tokenRs);
            return null;
        }
        if (null == devInfoRsDTO || !SUCCESS.equals(devInfoRsDTO.getState())) {
            LOG.error("权限中心获取token的返回结果不合法, tokenRs = {}", tokenRs);
            return null;
        }
        TokenDTO tokenInfoDTO = null;
        try {
            tokenInfoDTO = JSON.parseObject(devInfoRsDTO.getData(), TokenDTO.class);
        } catch (Exception e) {
            LOG.error("权限中心获取token返回结果中data字段转换异常,data = {}.", devInfoRsDTO.getData());
            e.getStackTrace();
            return null;
        }
        return tokenInfoDTO.getOauthToken();
    }


    /**
     * 采集 设备信息返回 json
     *
     * @param devInfo
     * @param isStrict true:全部节点全部采到才算成功　false:有节点失败也返回
     * @return null 没有符合采集的项目
     */
    public JSONObject collectDevice(DevInfoDTO devInfo, final boolean isStrict) throws IOException {
        System.out.println("要采集的设备:" + devInfo);

        //========获取要此厂商型号设备的对应的要采集的 oid 节点==============
        List<DevbusAccollectOid> devbusAccollectOids = getDevbusAccollectOids(devInfo);
        //========没有需要采集的直接返回null
        if (null == devbusAccollectOids) {
            return null;
        }

        JSONObject jsonObject = new JSONObject();
        JSONArray jsonArray = new JSONArray();

        //=======分别采集每个 oid 节点====================
        for (DevbusAccollectOid devbusAccollectOid : devbusAccollectOids) {
            //--------采集方式为get 返回 JSONObject, getnext String 返回 walk 则返回 JSONArray,
            if (devbusAccollectOid.getSnmpType() == 1) {
                String result = snmpGet(devInfo.getIpAddr(), devbusAccollectOid);
                if (true == isStrict) {
                    //获取数据失败或者 节点值为nosuchInstance(不存在返回)
                    if (null == result || result == "noSuchInstance") {
                        LOG.error("设备{}在严格模式下不能get到节点:{},终止采集",
                                devInfo.getIpAddr(), devbusAccollectOid.toString());
                        return null;
                    }
                }
                if (null == result) {
                    continue;
                }
                if ("noSuchInstance" == result) {
                    result = "";
                }
                jsonObject.put(devbusAccollectOid.getDbcKey(), result);
            } else if (devbusAccollectOid.getSnmpType() == 3) {
                jsonArray = snmpWalk(devInfo.getIpAddr(), devbusAccollectOid);
                if (true == isStrict) {
                    //获取数据失败或者 节点值为nosuchInstance(不存在返回)
                    if (null == jsonArray) {
                        LOG.error("设备{}在严格模式下不能walk到节点:{},终止采集",
                                devInfo.getIpAddr(), devbusAccollectOid.toString());
                        return null;
                    }
                }
                if (null == jsonArray) {
                    continue;
                }
                jsonObject.put(devbusAccollectOid.getDbcKey(), jsonArray);
            } else {
                throw new RuntimeException("未识别的采集方式");
            }
        }

        //=================返回数据=======================
        return jsonObject;
    }


    /**
     * 通过 get 方式获取数据
     *
     * @param devIp
     * @param devbusAccollectOid
     * @return null 失败
     * @throws IOException
     */
    private String snmpGet(String devIp, DevbusAccollectOid devbusAccollectOid) {
        SimpleSNMP simpleSNMP = new SimpleSNMP();

        //获取数据
        String response = null;
        try {
            response = simpleSNMP.get(devIp, "public", devbusAccollectOid.getOid());
        } catch (IOException e) {
            LOG.error("采集设备:{}网络IOException!!!!!!!!!失败信息:{}", devIp, e.getMessage());
        } catch (RuntimeException e) {
            LOG.error("采集设备:{}无响应!!!!!!!!!失败信息:{}", devIp, e.getMessage());
        }

        return response;
    }

    /**
     * 通过 walk 方式获取数据
     *
     * @param devIp
     * @param devbusAccollectOid
     * @return null 失败
     * @throws IOException
     */
    private JSONArray snmpWalk(String devIp, DevbusAccollectOid devbusAccollectOid) {
        SimpleSNMP simpleSNMP = new SimpleSNMP();

        //获取数据
        List<String> responses = null;
        JSONArray jsonArray = null;
        try {
            responses = simpleSNMP.walk(devIp, "public", devbusAccollectOid.getOid());

            //组装 json 字符串
            jsonArray = JSONArray.parseArray(JSON.toJSONString(responses));
        } catch (IOException e) {
            LOG.error("采集设备:{}网络IOException!!!!!!!!!失败信息:{}", devIp, e.getMessage());
        } catch (RuntimeException e) {
            LOG.error("采集设备:{}无响应!!!!!!!!!失败信息:{}", devIp, e.getMessage());
        }

        return jsonArray;
    }

    /**
     * 从数据库获取此厂家型号的 要采集的 oid 节点列表。
     *
     * @param devInfo
     * @return oid 节点列表,失败返回 null
     */
    private List<DevbusAccollectOid> getDevbusAccollectOids(DevInfoDTO devInfo) {
        try {
            //===通过 devbus_accollect_model_map 表查询数据中心此型号对应的本地型号id============
            //--------获取厂家型号id
            Integer vendorId = Integer.parseInt(devInfo.getCorporation());
            Integer modelId = Integer.parseInt(devInfo.getModel());
            //--------获取对应的型号信息
            List<DevbusAccollectModelMap> devbusAccollectModelMaps =
                    getDevbusAccollectModelMapsByDBCInfo(vendorId, modelId);


            //===如果没查到则使用modelId为0 的策略========================================
            if (devbusAccollectModelMaps.size() == 0) {
                LOG.debug("厂商为:{}(vendorId={}), 型号为:{}(modelId={})精确配对失败使用dbc_model=0进行模糊匹配",
                        devInfo.getCorporationText(), vendorId, devInfo.getModelText(), modelId);

                devbusAccollectModelMaps =
                        getDevbusAccollectModelMapsByDBCInfo(vendorId, 0);
            }

            //===如果modelId为0的策略不存在则属于没有对接的厂家设备一个信息也不需要采集
            if (devbusAccollectModelMaps.size() == 0) {
                LOG.debug("厂商为:{}(vendorId={}), 型号为:{}(modelId={})因为没有配对的设备型号所以不采集",
                        devInfo.getCorporationText(), vendorId, devInfo.getModelText(), modelId);

                return null;
            }
            LOG.debug("厂商为:{}(vendorId={}), 型号为:{}(modelId={})配对成功的",
                    devInfo.getCorporationText(), vendorId, devInfo.getModelText(), modelId);


            //===如果策略条数大于1,则策略有问题
            if (devbusAccollectModelMaps.size() > 1) {
                StringBuilder ids = new StringBuilder();
                for (DevbusAccollectModelMap devbusAccollectModelMap : devbusAccollectModelMaps) {
                    ids.append(devbusAccollectModelMap.getId());
                    ids.append(",");
                }

                LOG.error("配对策略冲突,冲突的选项id为:{}请查看 dbus_accollect_model_map", ids);
                return null;
            }


            //===配对型号成功 通过本次 modelId 查找满足的oids===========
            DevbusAccollectOidExample example = new DevbusAccollectOidExample();
            example.createCriteria().andModelIdEqualTo(devbusAccollectModelMaps.get(0).getModelId());
            return devbusAccollectOidMapper.selectByExample(example);
        } catch (NumberFormatException e) {
            LOG.error("获取厂家型号失败,Corporation={},model={}", devInfo.getCorporation(), devInfo.getModel());
            return null;
        }
    }


    /**
     * 通过 数据中心那边的 corpationId 和 modelId 获取本地型号信息
     *
     * @param dbcVendorId
     * @param dbcModelId
     * @return
     */
    private List<DevbusAccollectModelMap> getDevbusAccollectModelMapsByDBCInfo(Integer dbcVendorId, Integer dbcModelId) {
        DevbusAccollectModelMapExample mapExample = new DevbusAccollectModelMapExample();
        mapExample.createCriteria().andDbcCorporationEqualTo(dbcVendorId).andDbcModelEqualTo(dbcModelId);
        return devbusAccollectModelMapMapper.selectByExample(mapExample);
    }

    private void printResponse(ResponseEvent responseEvent) {
        System.out.println("----------解析Response-------------");

        if (null != responseEvent && null != responseEvent.getResponse()) {
            Vector<? extends VariableBinding> variableBindings = responseEvent.getResponse().getVariableBindings();
            for (int i = 0; i < variableBindings.size(); i++) {
                VariableBinding recVB = variableBindings.elementAt(i);
                System.out
                        .println(recVB.getOid() + " : " + recVB.getVariable().toString());
            }
        }
    }

    private void printResponses(List<ResponseEvent> responseEvents) {
        for (ResponseEvent responseEvent : responseEvents) {
            printResponse(responseEvent);
        }
    }


    private void printGetResult(DevbusAccollectOid oid, String responseSring) {
        System.out.println(String.format("get获取 oid=%s,value=%s", oid.toString(), responseSring));
    }

    private void printWalkResult(DevbusAccollectOid oid, List<String> responseList) {
        System.out.println(String.format("walk获取oid=%s", oid.toString()));
        for (String responseSring : responseList) {
            System.out.println(String.format("value=%s", responseSring));
        }
    }
}
